import { mockBuildCtx, mockCompilerCtx, mockValidatedConfig } from '@stencil/core/testing';
import path from 'path';

import type * as d from '../../../declarations';
import { patchTypescript } from '../../sys/typescript/typescript-sys';
import { generateAppTypes } from '../generate-app-types';
import { stubComponentCompilerEvent } from './ComponentCompilerEvent.stub';
import { stubComponentCompilerMeta } from './ComponentCompilerMeta.stub';
import { stubComponentCompilerProperty } from './ComponentCompilerProperty.stub';

describe('generateAppTypes', () => {
  let config: d.ValidatedConfig;
  let compilerCtx: d.CompilerCtx;
  let buildCtx: d.BuildCtx;
  let originalWriteFile: typeof compilerCtx.fs.writeFile;

  const mockWriteFile = jest.fn();

  beforeEach(() => {
    config = mockValidatedConfig({
      srcDir: '/',
    });
    compilerCtx = mockCompilerCtx(config);
    buildCtx = mockBuildCtx(config, compilerCtx);

    // Save the original write function to we can create a file in the
    // in-memory fs if needed
    originalWriteFile = compilerCtx.fs.writeFile;
    compilerCtx.fs.writeFile = mockWriteFile;

    mockWriteFile.mockResolvedValueOnce({ changedContent: true });
  });

  afterEach(() => {
    jest.resetAllMocks();
  });

  it('should generate a type declaration file without custom types', async () => {
    const compilerComponentMeta = stubComponentCompilerMeta({
      tagName: 'my-component',
      componentClassName: 'MyComponent',
      events: [],
    });
    buildCtx.components = [compilerComponentMeta];

    await generateAppTypes(config, compilerCtx, buildCtx, 'src');

    expect(mockWriteFile).toHaveBeenCalledWith(
      '/components.d.ts',
      `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
      {
        immediateWrite: true,
      },
    );
  });

  describe('custom event types', () => {
    it('should generate a type declaration file with custom event types', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        hasEvent: true,
        events: [stubComponentCompilerEvent()],
      });
      buildCtx.components = [compilerComponentMeta];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedEventType } from "./some/stubbed/path/resources";
export { UserImplementedEventType } from "./some/stubbed/path/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
    }
}
export interface MyComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyComponentElement;
}
declare global {
    interface HTMLMyComponentElementEventMap {
        "myEvent": UserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "onMyEvent"?: (event: MyComponentCustomEvent<UserImplementedEventType>) => void;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should generate a type declaration file with multiple custom events from the same location', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent(),
          stubComponentCompilerEvent({
            name: 'mySecondEvent',
            method: 'mySecondEvent',
            complexType: {
              original: 'SecondUserImplementedEventType',
              resolved: '"wee" | "woo"',
              references: {
                SecondUserImplementedEventType: {
                  id: './resources.ts::SecondUserImplementedEventType',
                  location: 'import',
                  path: './resources',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { SecondUserImplementedEventType, UserImplementedEventType } from "./some/stubbed/path/resources";
export { SecondUserImplementedEventType, UserImplementedEventType } from "./some/stubbed/path/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
    }
}
export interface MyComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyComponentElement;
}
declare global {
    interface HTMLMyComponentElementEventMap {
        "myEvent": UserImplementedEventType;
        "mySecondEvent": SecondUserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "onMyEvent"?: (event: MyComponentCustomEvent<UserImplementedEventType>) => void;
        "onMySecondEvent"?: (event: MyComponentCustomEvent<SecondUserImplementedEventType>) => void;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should generate a type declaration file with multiple components using the same custom event type', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent({
            complexType: {
              original: 'UserImplementedEventType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedEventType: {
                  location: 'import',
                  id: './resources.ts::UserImplementedEventType',
                  path: './resources',
                },
              },
            },
          }),
        ],
      });
      const compilerComponentMeta2 = stubComponentCompilerMeta({
        tagName: 'my-new-component',
        componentClassName: 'MyNewComponent',
        jsFilePath: '/some/stubbed/path/nested/my-component.js',
        sourceFilePath: '/some/stubbed/path/nested/my-component.tsx',
        sourceMapPath: '/some/stubbed/path/nested/my-component.js.map',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent({
            complexType: {
              original: 'UserImplementedEventType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedEventType: {
                  location: 'import',
                  id: 'placeholder',
                  path: '../resources',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta, compilerComponentMeta2];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedEventType } from "./some/stubbed/path/resources";
export { UserImplementedEventType } from "./some/stubbed/path/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
    }
    /**
     * docs
     */
    interface MyNewComponent {
    }
}
export interface MyComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyComponentElement;
}
export interface MyNewComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyNewComponentElement;
}
declare global {
    interface HTMLMyComponentElementEventMap {
        "myEvent": UserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLMyNewComponentElementEventMap {
        "myEvent": UserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyNewComponentElement extends Components.MyNewComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyNewComponentElementEventMap>(type: K, listener: (this: HTMLMyNewComponentElement, ev: MyNewComponentCustomEvent<HTMLMyNewComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyNewComponentElementEventMap>(type: K, listener: (this: HTMLMyNewComponentElement, ev: MyNewComponentCustomEvent<HTMLMyNewComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyNewComponentElement: {
        prototype: HTMLMyNewComponentElement;
        new (): HTMLMyNewComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
        "my-new-component": HTMLMyNewComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "onMyEvent"?: (event: MyComponentCustomEvent<UserImplementedEventType>) => void;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "onMyEvent"?: (event: MyNewComponentCustomEvent<UserImplementedEventType>) => void;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
        "my-new-component": MyNewComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
            /**
             * docs
             */
            "my-new-component": LocalJSX.MyNewComponent & JSXBase.HTMLAttributes<HTMLMyNewComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should handle custom event type name collisions when defined in separate files', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        jsFilePath: '/some/stubbed/path/a/my-component.js',
        sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
        sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent({
            complexType: {
              original: 'UserImplementedEventType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedEventType: {
                  location: 'import',
                  path: './resources',
                  id: './resources::UserImplementedEventType',
                },
              },
            },
          }),
        ],
      });
      const compilerComponentMeta2 = stubComponentCompilerMeta({
        tagName: 'my-new-component',
        componentClassName: 'MyNewComponent',
        jsFilePath: '/some/stubbed/path/b/my-new-component.js',
        sourceFilePath: '/some/stubbed/path/b/my-new-component.tsx',
        sourceMapPath: '/some/stubbed/path/b/my-new-component.js.map',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent({
            complexType: {
              original: 'UserImplementedEventType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedEventType: {
                  location: 'import',
                  path: './resources',
                  id: './resources::UserImplementedEventType',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta, compilerComponentMeta2];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedEventType } from "./some/stubbed/path/a/resources";
import { UserImplementedEventType as UserImplementedEventType1 } from "./some/stubbed/path/b/resources";
export { UserImplementedEventType } from "./some/stubbed/path/a/resources";
export { UserImplementedEventType as UserImplementedEventType1 } from "./some/stubbed/path/b/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
    }
    /**
     * docs
     */
    interface MyNewComponent {
    }
}
export interface MyComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyComponentElement;
}
export interface MyNewComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyNewComponentElement;
}
declare global {
    interface HTMLMyComponentElementEventMap {
        "myEvent": UserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLMyNewComponentElementEventMap {
        "myEvent": UserImplementedEventType1;
    }
    /**
     * docs
     */
    interface HTMLMyNewComponentElement extends Components.MyNewComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyNewComponentElementEventMap>(type: K, listener: (this: HTMLMyNewComponentElement, ev: MyNewComponentCustomEvent<HTMLMyNewComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyNewComponentElementEventMap>(type: K, listener: (this: HTMLMyNewComponentElement, ev: MyNewComponentCustomEvent<HTMLMyNewComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyNewComponentElement: {
        prototype: HTMLMyNewComponentElement;
        new (): HTMLMyNewComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
        "my-new-component": HTMLMyNewComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "onMyEvent"?: (event: MyComponentCustomEvent<UserImplementedEventType>) => void;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "onMyEvent"?: (event: MyNewComponentCustomEvent<UserImplementedEventType1>) => void;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
        "my-new-component": MyNewComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
            /**
             * docs
             */
            "my-new-component": LocalJSX.MyNewComponent & JSXBase.HTMLAttributes<HTMLMyNewComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should handle custom event type name collisions when defined in the component files', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        jsFilePath: '/some/stubbed/path/a/my-component.js',
        sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
        sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent({
            complexType: {
              original: 'UserImplementedEventType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedEventType: {
                  location: 'local',
                  path: '/some/stubbed/path/a/my-component.tsx',
                  id: '/some/stubbed/path/a/my-component.tsx::UserImplementedEventType',
                },
              },
            },
          }),
        ],
      });
      const compilerComponentMeta2 = stubComponentCompilerMeta({
        tagName: 'my-new-component',
        componentClassName: 'MyNewComponent',
        jsFilePath: '/some/stubbed/path/b/my-new-component.js',
        sourceFilePath: '/some/stubbed/path/b/my-new-component.tsx',
        sourceMapPath: '/some/stubbed/path/b/my-new-component.js.map',
        hasEvent: true,
        events: [
          stubComponentCompilerEvent({
            complexType: {
              original: 'UserImplementedEventType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedEventType: {
                  location: 'local',
                  id: '/some/stubbed/path/b/my-new-component.tsx::UserImplementedEventType',
                  path: '/some/stubbed/path/b/my-new-component.tsx',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta, compilerComponentMeta2];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedEventType } from "./some/stubbed/path/a/my-component";
import { UserImplementedEventType as UserImplementedEventType1 } from "./some/stubbed/path/b/my-new-component";
export { UserImplementedEventType } from "./some/stubbed/path/a/my-component";
export { UserImplementedEventType as UserImplementedEventType1 } from "./some/stubbed/path/b/my-new-component";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
    }
    /**
     * docs
     */
    interface MyNewComponent {
    }
}
export interface MyComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyComponentElement;
}
export interface MyNewComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyNewComponentElement;
}
declare global {
    interface HTMLMyComponentElementEventMap {
        "myEvent": UserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLMyNewComponentElementEventMap {
        "myEvent": UserImplementedEventType1;
    }
    /**
     * docs
     */
    interface HTMLMyNewComponentElement extends Components.MyNewComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyNewComponentElementEventMap>(type: K, listener: (this: HTMLMyNewComponentElement, ev: MyNewComponentCustomEvent<HTMLMyNewComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyNewComponentElementEventMap>(type: K, listener: (this: HTMLMyNewComponentElement, ev: MyNewComponentCustomEvent<HTMLMyNewComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyNewComponentElement: {
        prototype: HTMLMyNewComponentElement;
        new (): HTMLMyNewComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
        "my-new-component": HTMLMyNewComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "onMyEvent"?: (event: MyComponentCustomEvent<UserImplementedEventType>) => void;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "onMyEvent"?: (event: MyNewComponentCustomEvent<UserImplementedEventType1>) => void;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
        "my-new-component": MyNewComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
            /**
             * docs
             */
            "my-new-component": LocalJSX.MyNewComponent & JSXBase.HTMLAttributes<HTMLMyNewComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });
  });

  describe('custom prop types', () => {
    it('should export prop types too', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'name',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedPropType: {
                  location: 'import',
                  path: './resources',
                  id: './resources::UserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/resources";
export { UserImplementedPropType } from "./some/stubbed/path/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should generate a type declaration file with multiple custom prop types from the same location', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'name',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedPropType: {
                  location: 'import',
                  path: './resources',
                  id: './resources::UserImplementedPropType',
                },
              },
            },
          }),
          stubComponentCompilerProperty({
            name: 'email',
            complexType: {
              original: 'SecondUserImplementedPropType',
              resolved: '"wee" | "woo"',
              references: {
                SecondUserImplementedPropType: {
                  location: 'import',
                  path: './resources',
                  id: './resources::SecondUserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { SecondUserImplementedPropType, UserImplementedPropType } from "./some/stubbed/path/resources";
export { SecondUserImplementedPropType, UserImplementedPropType } from "./some/stubbed/path/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "email": SecondUserImplementedPropType;
        "name": UserImplementedPropType;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "email"?: SecondUserImplementedPropType;
        "name"?: UserImplementedPropType;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should generate a type declaration file with multiple components using the same custom prop type', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'name',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedPropType: {
                  location: 'import',
                  path: './resources',
                  id: './resources::UserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      const compilerComponentMeta2 = stubComponentCompilerMeta({
        tagName: 'my-new-component',
        componentClassName: 'MyNewComponent',
        jsFilePath: '/some/stubbed/path/nested/my-component.js',
        sourceFilePath: '/some/stubbed/path/nested/my-component.tsx',
        sourceMapPath: '/some/stubbed/path/nested/my-component.js.map',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'fullName',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedPropType: {
                  location: 'import',
                  path: '../resources',
                  id: '../resources::UserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta, compilerComponentMeta2];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/resources";
export { UserImplementedPropType } from "./some/stubbed/path/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "fullName": UserImplementedPropType;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    /**
     * docs
     */
    interface HTMLMyNewComponentElement extends Components.MyNewComponent, HTMLStencilElement {
    }
    var HTMLMyNewComponentElement: {
        prototype: HTMLMyNewComponentElement;
        new (): HTMLMyNewComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
        "my-new-component": HTMLMyNewComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "fullName"?: UserImplementedPropType;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
        "my-new-component": MyNewComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
            /**
             * docs
             */
            "my-new-component": LocalJSX.MyNewComponent & JSXBase.HTMLAttributes<HTMLMyNewComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should handle custom prop type name collisions when defined in separate files', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        jsFilePath: '/some/stubbed/path/a/my-component.js',
        sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
        sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'name',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedPropType: {
                  location: 'import',
                  id: './resources.ts::UserImplementedPropType',
                  path: './resources',
                },
              },
            },
          }),
        ],
      });
      const compilerComponentMeta2 = stubComponentCompilerMeta({
        tagName: 'my-new-component',
        componentClassName: 'MyNewComponent',
        jsFilePath: '/some/stubbed/path/b/my-new-component.js',
        sourceFilePath: '/some/stubbed/path/b/my-new-component.tsx',
        sourceMapPath: '/some/stubbed/path/b/my-new-component.js.map',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'newName',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"wee" | "woo"',
              references: {
                UserImplementedPropType: {
                  location: 'import',
                  path: './resources',
                  id: './resources.ts::UserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta, compilerComponentMeta2];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/a/resources";
import { UserImplementedPropType as UserImplementedPropType1 } from "./some/stubbed/path/b/resources";
export { UserImplementedPropType } from "./some/stubbed/path/a/resources";
export { UserImplementedPropType as UserImplementedPropType1 } from "./some/stubbed/path/b/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "newName": UserImplementedPropType1;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    /**
     * docs
     */
    interface HTMLMyNewComponentElement extends Components.MyNewComponent, HTMLStencilElement {
    }
    var HTMLMyNewComponentElement: {
        prototype: HTMLMyNewComponentElement;
        new (): HTMLMyNewComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
        "my-new-component": HTMLMyNewComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "newName"?: UserImplementedPropType1;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
        "my-new-component": MyNewComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
            /**
             * docs
             */
            "my-new-component": LocalJSX.MyNewComponent & JSXBase.HTMLAttributes<HTMLMyNewComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });

    it('should handle custom prop type name collisions when defined in the component files', async () => {
      const compilerComponentMeta = stubComponentCompilerMeta({
        tagName: 'my-component',
        componentClassName: 'MyComponent',
        jsFilePath: '/some/stubbed/path/a/my-component.js',
        sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
        sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'name',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"foo" | "bar"',
              references: {
                UserImplementedPropType: {
                  location: 'local',
                  path: '/some/stubbed/path/a/my-component.tsx',
                  id: '/some/stubbed/path/a/my-component.tsx::UserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      const compilerComponentMeta2 = stubComponentCompilerMeta({
        tagName: 'my-new-component',
        componentClassName: 'MyNewComponent',
        jsFilePath: '/some/stubbed/path/b/my-new-component.js',
        sourceFilePath: '/some/stubbed/path/b/my-new-component.tsx',
        sourceMapPath: '/some/stubbed/path/b/my-new-component.js.map',
        hasProp: true,
        properties: [
          stubComponentCompilerProperty({
            name: 'name',
            complexType: {
              original: 'UserImplementedPropType',
              resolved: '"wee" | "woo"',
              references: {
                UserImplementedPropType: {
                  location: 'local',
                  path: '/some/stubbed/path/b/my-new-component.tsx',
                  id: '/some/stubbed/path/b/my-new-component.tsx::UserImplementedPropType',
                },
              },
            },
          }),
        ],
      });
      buildCtx.components = [compilerComponentMeta, compilerComponentMeta2];

      await generateAppTypes(config, compilerCtx, buildCtx, 'src');

      expect(mockWriteFile).toHaveBeenCalledWith(
        '/components.d.ts',
        `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/a/my-component";
import { UserImplementedPropType as UserImplementedPropType1 } from "./some/stubbed/path/b/my-new-component";
export { UserImplementedPropType } from "./some/stubbed/path/a/my-component";
export { UserImplementedPropType as UserImplementedPropType1 } from "./some/stubbed/path/b/my-new-component";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "name": UserImplementedPropType1;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    /**
     * docs
     */
    interface HTMLMyNewComponentElement extends Components.MyNewComponent, HTMLStencilElement {
    }
    var HTMLMyNewComponentElement: {
        prototype: HTMLMyNewComponentElement;
        new (): HTMLMyNewComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
        "my-new-component": HTMLMyNewComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    /**
     * docs
     */
    interface MyNewComponent {
        "name"?: UserImplementedPropType1;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
        "my-new-component": MyNewComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
            /**
             * docs
             */
            "my-new-component": LocalJSX.MyNewComponent & JSXBase.HTMLAttributes<HTMLMyNewComponentElement>;
        }
    }
}
`,
        {
          immediateWrite: true,
        },
      );
    });
  });

  it('should work with both event and prop types', async () => {
    const compilerComponentMeta = stubComponentCompilerMeta({
      tagName: 'my-component',
      componentClassName: 'MyComponent',
      jsFilePath: '/some/stubbed/path/a/my-component.js',
      sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
      sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
      hasProp: true,
      hasEvent: true,
      events: [stubComponentCompilerEvent()],
      properties: [
        stubComponentCompilerProperty({
          name: 'name',
          complexType: {
            original: 'UserImplementedPropType',
            resolved: '"foo" | "bar"',
            references: {
              UserImplementedPropType: {
                location: 'import',
                path: './resources',
                id: './resources.ts::UserImplementedPropType',
              },
            },
          },
        }),
      ],
    });
    buildCtx.components = [compilerComponentMeta];

    await generateAppTypes(config, compilerCtx, buildCtx, 'src');

    expect(mockWriteFile).toHaveBeenCalledWith(
      '/components.d.ts',
      `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedEventType, UserImplementedPropType } from "./some/stubbed/path/a/resources";
export { UserImplementedEventType, UserImplementedPropType } from "./some/stubbed/path/a/resources";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
}
export interface MyComponentCustomEvent<T> extends CustomEvent<T> {
    detail: T;
    target: HTMLMyComponentElement;
}
declare global {
    interface HTMLMyComponentElementEventMap {
        "myEvent": UserImplementedEventType;
    }
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
        addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
        addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
        removeEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
        removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
        "onMyEvent"?: (event: MyComponentCustomEvent<UserImplementedEventType>) => void;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
      {
        immediateWrite: true,
      },
    );
  });

  it('should transform aliased paths if transformAliasedImportPaths is true', async () => {
    const compilerComponentMeta = stubComponentCompilerMeta({
      tagName: 'my-component',
      componentClassName: 'MyComponent',
      jsFilePath: path.join(config.rootDir, 'some/stubbed/path/a/my-component.js'),
      sourceFilePath: path.join(config.rootDir, 'some/stubbed/path/a/my-component.tsx'),
      sourceMapPath: path.join(config.rootDir, 'some/stubbed/path/a/my-component.js.map'),
      hasProp: true,
      properties: [
        stubComponentCompilerProperty({
          name: 'name',
          complexType: {
            original: 'UserImplementedPropType',
            resolved: '"foo" | "bar"',
            references: {
              UserImplementedPropType: {
                id: 'some-module.ts::UserImplementedPropType',
                location: 'import',
                path: '@utils',
              },
            },
          },
        }),
      ],
    });
    buildCtx.components = [compilerComponentMeta];
    config.tsCompilerOptions = {
      paths: {
        '@utils': [path.join(config.rootDir, 'some/stubbed/path/utils/utils.ts')],
      },
      declaration: true,
    };
    config.transformAliasedImportPaths = true;
    // We need to have a file in the in-memory fs for the TS module resolution to succeed
    await originalWriteFile(path.join(config.rootDir, 'some/stubbed/path/utils/utils.ts'), '');

    patchTypescript(config, compilerCtx.fs);

    await generateAppTypes(config, compilerCtx, buildCtx, 'src');

    expect(mockWriteFile).toHaveBeenCalledWith(
      '/components.d.ts',
      `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/utils/utils";
export { UserImplementedPropType } from "./some/stubbed/path/utils/utils";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
      {
        immediateWrite: true,
      },
    );
  });

  it('should not transform aliased paths if transformAliasedImportPaths is false', async () => {
    const compilerComponentMeta = stubComponentCompilerMeta({
      tagName: 'my-component',
      componentClassName: 'MyComponent',
      jsFilePath: '/some/stubbed/path/a/my-component.js',
      sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
      sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
      hasProp: true,
      properties: [
        stubComponentCompilerProperty({
          name: 'name',
          complexType: {
            original: 'UserImplementedPropType',
            resolved: '"foo" | "bar"',
            references: {
              UserImplementedPropType: {
                id: 'some-file.ts::UserImplementedPropType',
                location: 'import',
                path: '@utils',
              },
            },
          },
        }),
      ],
    });
    buildCtx.components = [compilerComponentMeta];
    config.tsCompilerOptions = {};

    await generateAppTypes(config, compilerCtx, buildCtx, 'src');

    expect(mockWriteFile).toHaveBeenCalledWith(
      '/components.d.ts',
      `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "@utils";
export { UserImplementedPropType } from "@utils";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
      {
        immediateWrite: true,
      },
    );
  });

  it('should handle type import aliases', async () => {
    const compilerComponentMeta = stubComponentCompilerMeta({
      tagName: 'my-component',
      componentClassName: 'MyComponent',
      jsFilePath: '/some/stubbed/path/a/my-component.js',
      sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
      sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
      hasProp: true,
      properties: [
        stubComponentCompilerProperty({
          name: 'name',
          complexType: {
            original: 'UserImplementedPropType',
            resolved: '"foo" | "bar"',
            references: {
              UserImplementedPropType: {
                id: 'some-file.ts::MyType',
                location: 'import',
                path: '@utils',
              },
              Fragment: {
                location: 'import',
                path: '@stencil/core',
                id: '',
              },
            },
          },
        }),
      ],
    });
    buildCtx.components = [compilerComponentMeta];
    config.tsCompilerOptions = {};

    await generateAppTypes(config, compilerCtx, buildCtx, 'src');

    console.log(mockWriteFile.mock.calls[0][1]);

    expect(mockWriteFile).toHaveBeenCalledWith(
      '/components.d.ts',
      `/* eslint-disable */
/* tslint:disable */
/**
 * This is an autogenerated file created by the Stencil compiler.
 * It contains typing information for all components that exist in this project.
 */
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { MyType as UserImplementedPropType } from "@utils";
import { Fragment } from "@stencil/core";
export { MyType as UserImplementedPropType } from "@utils";
export { Fragment } from "@stencil/core";
export namespace Components {
    /**
     * docs
     */
    interface MyComponent {
        "name": UserImplementedPropType;
    }
}
declare global {
    /**
     * docs
     */
    interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
    }
    var HTMLMyComponentElement: {
        prototype: HTMLMyComponentElement;
        new (): HTMLMyComponentElement;
    };
    interface HTMLElementTagNameMap {
        "my-component": HTMLMyComponentElement;
    }
}
declare namespace LocalJSX {
    /**
     * docs
     */
    interface MyComponent {
        "name"?: UserImplementedPropType;
    }
    interface IntrinsicElements {
        "my-component": MyComponent;
    }
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
    export namespace JSX {
        interface IntrinsicElements {
            /**
             * docs
             */
            "my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
        }
    }
}
`,
      {
        immediateWrite: true,
      },
    );
  });
});
